package com.techsoft.Interceptor;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.techsoft.SQLIDRejectedException;
import com.techsoft.SQLObject;
import com.techsoft.Session;

public class InterceptorManager {
	private static final Logger Log = LoggerFactory
			.getLogger(InterceptorManager.class);

	private static InterceptorManager instance = new InterceptorManager();
	private List<SQLIDInterceptor> globalInterceptors = new CopyOnWriteArrayList<SQLIDInterceptor>();
	private Map<String, List<SQLIDInterceptor>> usersInterceptors = new ConcurrentHashMap<String, List<SQLIDInterceptor>>();

	public static InterceptorManager getInstance() {
		return instance;
	}

	public List<SQLIDInterceptor> getInterceptors() {
		return Collections.unmodifiableList(globalInterceptors);
	}

	public void addInterceptor(SQLIDInterceptor interceptor) {
		if (interceptor == null) {
			throw new NullPointerException("Parameter interceptor was null.");
		}

		if (globalInterceptors.contains(interceptor)) {
			globalInterceptors.remove(interceptor);
		}
		globalInterceptors.add(interceptor);
	}

	public void addInterceptor(int index, SQLIDInterceptor interceptor) {
		if (index < 0 || (index > globalInterceptors.size())) {
			throw new IndexOutOfBoundsException("Index " + index + " invalid.");
		}
		if (interceptor == null) {
			throw new NullPointerException("Parameter interceptor was null.");
		}

		if (globalInterceptors.contains(interceptor)) {
			int oldIndex = globalInterceptors.indexOf(interceptor);
			if (oldIndex < index) {
				index -= 1;
			}
			globalInterceptors.remove(interceptor);
		}

		globalInterceptors.add(index, interceptor);
	}

	public boolean removeInterceptor(SQLIDInterceptor interceptor) {
		return globalInterceptors.remove(interceptor);
	}

	public List<SQLIDInterceptor> getUserInterceptors(String username) {
		List<SQLIDInterceptor> userInterceptors = usersInterceptors
				.get(username);
		if (userInterceptors == null) {
			return Collections.emptyList();
		} else {
			return Collections.unmodifiableList(userInterceptors);
		}
	}

	public void addUserInterceptor(String username, int index,
			SQLIDInterceptor interceptor) {
		List<SQLIDInterceptor> userInterceptors = usersInterceptors
				.get(username);
		if (userInterceptors == null) {
			userInterceptors = new CopyOnWriteArrayList<SQLIDInterceptor>();
			usersInterceptors.put(username, userInterceptors);
		} else {
			if (index < 0 || (index > userInterceptors.size())) {
				throw new IndexOutOfBoundsException("Index " + index
						+ " invalid.");
			}
			if (interceptor == null) {
				throw new NullPointerException(
						"Parameter interceptor was null.");
			}

			if (userInterceptors.contains(interceptor)) {
				int oldIndex = userInterceptors.indexOf(interceptor);
				if (oldIndex < index) {
					index -= 1;
				}
				userInterceptors.remove(interceptor);
			}
		}
		userInterceptors.add(index, interceptor);
	}

	public boolean removeUserInterceptor(String username,
			SQLIDInterceptor interceptor) {
		boolean answer = false;
		List<SQLIDInterceptor> userInterceptors = usersInterceptors
				.get(username);
		if (userInterceptors != null) {
			answer = userInterceptors.remove(interceptor);
			if (userInterceptors.isEmpty()) {
				usersInterceptors.remove(username);
			}
		}
		return answer;
	}

	public void invokeInterceptors(SQLObject sqlobj, Session session,
			SQLIDInterceptor.DMLType operator, boolean processed)
			throws SQLIDRejectedException {
		if (!globalInterceptors.isEmpty()) {
			for (SQLIDInterceptor interceptor : globalInterceptors) {
				try {
					interceptor.interceptSQLID(sqlobj, session, operator,
							processed);
				} catch (SQLIDRejectedException e) {
					if (processed) {
						Log.error("interceptor cannot reject sqlobj.", e);
					} else {
						throw e;
					}
				} catch (Throwable e) {
					Log.error("Error in interceptor: " + interceptor
							+ " while intercepting: " + sqlobj, e);
				}
			}
		}

		if (usersInterceptors.isEmpty()) {
			return;
		}

		String username = String.valueOf(session.getUserInfo().get(
				Session.userNameField));
		if (username != null) {
			Collection<SQLIDInterceptor> userInterceptors = usersInterceptors
					.get(username);
			if (userInterceptors != null && !userInterceptors.isEmpty()) {
				for (SQLIDInterceptor interceptor : userInterceptors) {
					try {
						interceptor.interceptSQLID(sqlobj, session, operator,
								processed);
					} catch (SQLIDRejectedException e) {
						if (processed) {
							Log.error("interceptor cannot reject sqlobj.", e);
						} else {
							throw e;
						}
					} catch (Throwable e) {
						Log.error("Error in interceptor: " + interceptor
								+ " while intercepting: " + sqlobj, e);
					}
				}
			}
		}
	}
}
